﻿/*************************************************************************************
 * 文 件 名:   IHK_FileLoadUtility
 * 
 * 描    述:   加载图片、视频、音频的工具（应用了QF），该工具仅可用于PC端
 * 
 * 版    本：  V1.0
 * 创 建 者：  京产肠饭
 * 创建时间：  2022/5/13
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Networking;
using QFramework;
using System.IO;
using UnityEditor;

namespace HKTools
{
    public interface IHK_FileLoadUtility : IUtility
    {
        /// <summary>
        /// 在磁盘中选择对应路径图片加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：读取到的Texture2D 及 图片对应路径
        /// </summary>
        void LoadImage(Action<Texture2D, string> callback);

        /// <summary>
        /// 在磁盘中选择对应路径视频加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：视频对应路径
        /// </summary>
        void LoadVideo(Action<string> callback);

        /// <summary>
        /// 在磁盘中选择对应路径音频加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：音频对应路径
        /// </summary>
        void LoadAudio(Action<AudioClip, string> callback);
    }

    public class HK_FileLoadUtility : IHK_FileLoadUtility
    {
        SourceLoader sourceLoader;

        /// <summary>
        /// 在磁盘中选择对应路径图片加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：读取到的Texture2D 及 图片对应路径
        /// </summary>
        public void LoadImage(Action<Texture2D, string> callback)
        {
            OpenFileDlg fileDialog = new OpenFileDlg();
            fileDialog.structSize = Marshal.SizeOf(fileDialog);
            fileDialog.filter = "图片文件(*.jpg,*.png)\0*.jpg;*.png";
            fileDialog.file = new string(new char[256]);
            fileDialog.maxFile = fileDialog.file.Length;
            fileDialog.fileTitle = new string(new char[64]);
            fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
            fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            fileDialog.title = "选择文件";
            fileDialog.defExt = "png";//显示文件的类型
            fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
            fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

            if (OpenFileDialog.GetOpenFileName(fileDialog))
            {
                string filepath = fileDialog.file; //选择的文件路径; 
                string fileName = fileDialog.fileTitle;

                if (sourceLoader == null)
                    sourceLoader = new GameObject().AddComponent<SourceLoader>();

                sourceLoader.LoadImg(fileName, filepath, callback);
            }
        }

        /// <summary>
        /// 在磁盘中选择对应路径视频加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：视频对应路径
        /// </summary>
        public void LoadVideo(Action<string> callback)
        {
            OpenFileDlg fileDialog = new OpenFileDlg();
            fileDialog.structSize = Marshal.SizeOf(fileDialog);
            fileDialog.filter = "视频文件(*.mp4,*.mov)\0*.mp4;*.mov";
            fileDialog.file = new string(new char[256]);
            fileDialog.maxFile = fileDialog.file.Length;
            fileDialog.fileTitle = new string(new char[64]);
            fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
            fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            fileDialog.title = "选择文件";
            fileDialog.defExt = "mp4";//显示文件的类型
            fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
            fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

            if (OpenFileDialog.GetOpenFileName(fileDialog))
            {
                string filepath = fileDialog.file; //选择的文件路径; 
                string fileName = fileDialog.fileTitle;

                if (sourceLoader == null)
                    sourceLoader = new GameObject().AddComponent<SourceLoader>();

                sourceLoader.LoadVideo(fileName, filepath, callback);
            }
        }

        /// <summary>
        /// 在磁盘中选择对应路径音频加载，并存储在StreamingAssets文件夹中
        /// 回调函数将返回：音频对应路径
        /// </summary>
        public void LoadAudio(Action<AudioClip, string> callback)
        {
            OpenFileDlg fileDialog = new OpenFileDlg();
            fileDialog.structSize = Marshal.SizeOf(fileDialog);
            fileDialog.filter = "音频文件(*.mp3,*.ogg)\0*.mp3;*.ogg";
            fileDialog.file = new string(new char[256]);
            fileDialog.maxFile = fileDialog.file.Length;
            fileDialog.fileTitle = new string(new char[64]);
            fileDialog.maxFileTitle = fileDialog.fileTitle.Length;
            fileDialog.initialDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            fileDialog.title = "选择文件";
            fileDialog.defExt = "mp3";//显示文件的类型
            fileDialog.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
            fileDialog.dlgOwner = OpenFileDialog.GetForegroundWindow();

            if (OpenFileDialog.GetOpenFileName(fileDialog))
            {
                string filepath = fileDialog.file; //选择的文件路径; 
                string fileName = fileDialog.fileTitle;

                if (sourceLoader == null)
                    sourceLoader = new GameObject().AddComponent<SourceLoader>();

                sourceLoader.LoadAudio(fileName, filepath, callback);
            }
        }

        public class SourceLoader : MonoBehaviour
        {
            public void LoadImg(string imgName, string imgPath, Action<Texture2D, string> callback)
            {
                StartCoroutine(LoadImg_IE(imgName, imgPath, callback));
            }

            public void LoadVideo(string videoName, string videoPath, Action<string> callback)
            {
                StartCoroutine(LoadVideo_IE(videoName, videoPath, callback));
            }

            public void LoadAudio(string audioName, string audioPath, Action<AudioClip, string> callback)
            {
                StartCoroutine(LoadAudio_IE(audioName, audioPath, callback));
            }

            IEnumerator LoadImg_IE(string imgName, string imgPath, Action<Texture2D, string> callback)
            {
                if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadImgs"))
                    Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadImgs");

                string storePath = Application.streamingAssetsPath + "/LoadImgs/" + imgName;

                //创建一个负责读取的流
                using (FileStream fsRead = new FileStream(imgPath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    //创建一个负责写入的流
                    using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        // 5 兆的字节数组
                        byte[] buffer = new byte[1024 * 1024 * 5];
                        //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                        while (true)
                        {
                            //返回本次读取实际读取到的字节数
                            int r = fsRead.Read(buffer, 0, buffer.Length);
                            //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                            if (r == 0)
                                break;

                            fdWrite.Write(buffer, 0, r);
                        }
                    }
                }

                yield return null;
#if UNITY_EDITOR
                AssetDatabase.Refresh();
#endif
                using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(storePath))
                {
                    yield return request.SendWebRequest();

                    if (request.error != null)
                        Debug.Log(request.error);
                    else if (request.isDone)
                    {
                        Texture2D texture = DownloadHandlerTexture.GetContent(request);
                        callback?.Invoke(texture, storePath);
                    }
                }
            }

            IEnumerator LoadVideo_IE(string videoName, string videoPath, Action<string> callback)
            {
                if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadVideos"))
                    Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadVideos");

                string storePath = Application.streamingAssetsPath + "/LoadVideos/" + videoName;

                //创建一个负责读取的流
                using (FileStream fsRead = new FileStream(videoPath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    //创建一个负责写入的流
                    using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        // 5 兆的字节数组
                        byte[] buffer = new byte[1024 * 1024 * 5];
                        //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                        while (true)
                        {
                            //返回本次读取实际读取到的字节数
                            int r = fsRead.Read(buffer, 0, buffer.Length);
                            //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                            if (r == 0)
                                break;

                            fdWrite.Write(buffer, 0, r);
                        }
                    }
                }

                yield return null;
#if UNITY_EDITOR
                AssetDatabase.Refresh();
#endif
                callback(storePath);
            }

            IEnumerator LoadAudio_IE(string audioName, string audioPath, Action<AudioClip, string> callback)
            {
                if (!Directory.Exists(Application.streamingAssetsPath + "\\LoadAudios"))
                    Directory.CreateDirectory(Application.streamingAssetsPath + "\\LoadAudios");

                string storePath = Application.streamingAssetsPath + "/LoadAudios/" + audioName;

                //创建一个负责读取的流
                using (FileStream fsRead = new FileStream(audioPath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    //创建一个负责写入的流
                    using (FileStream fdWrite = new FileStream(storePath, FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        // 5 兆的字节数组
                        byte[] buffer = new byte[1024 * 1024 * 5];
                        //因为文件可能会比较大，所以我们在读取的时候 应该通过一个循环去读取
                        while (true)
                        {
                            //返回本次读取实际读取到的字节数
                            int r = fsRead.Read(buffer, 0, buffer.Length);
                            //如果返回一个0  也就意味着什么都没有读取到 即读取完了
                            if (r == 0)
                                break;

                            fdWrite.Write(buffer, 0, r);
                        }
                    }
                }

                yield return null;
#if UNITY_EDITOR
                AssetDatabase.Refresh();
#endif
                AudioClip audioClip = null;
                UnityWebRequest request;

                string audioExtention = audioName.Split('.')[1];
                if (audioExtention == "mp3")
                    request = UnityWebRequestMultimedia.GetAudioClip(storePath, AudioType.MPEG);
                else
                    request = UnityWebRequestMultimedia.GetAudioClip(storePath, AudioType.OGGVORBIS);

                yield return request.SendWebRequest();
                if (request.error != null)
                    Debug.Log(request.error.ToString());
                else
                    audioClip = DownloadHandlerAudioClip.GetContent(request);

                callback(audioClip, storePath);
            }
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class OpenFileDlg
    {
        public int structSize = 0;
        public IntPtr dlgOwner = IntPtr.Zero;
        public IntPtr instance = IntPtr.Zero;
        public String filter = null;
        public String customFilter = null;
        public int maxCustFilter = 0;
        public int filterIndex = 0;
        public String file = null;
        public int maxFile = 0;
        public String fileTitle = null;
        public int maxFileTitle = 0;
        public String initialDir = null;
        public String title = null;
        public int flags = 0;
        public short fileOffset = 0;
        public short fileExtension = 0;
        public String defExt = null;
        public IntPtr custData = IntPtr.Zero;
        public IntPtr hook = IntPtr.Zero;
        public String templateName = null;
        public IntPtr reservedPtr = IntPtr.Zero;
        public int reservedInt = 0;
        public int flagsEx = 0;
    }

    public class OpenFileDialog
    {
        [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern bool GetOpenFileName([In, Out] OpenFileDlg ofd);
        [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
        public static extern bool GetSaveFileName([In, Out] OpenFileDlg ofd);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();
    }

    public static class Texture2DExt
    {
        public static Sprite ToSprite(this Texture2D self)
        {
            var rect = new Rect(0, 0, self.width, self.height);
            var pivot = Vector2.one * 0.5f;
            var newSprite = Sprite.Create(self, rect, pivot);

            return newSprite;
        }
    }
}